#include "stdafx.h"
#include <boost/test/execution_monitor.hpp>
#include "detail/bmock_test_mode_control.hpp"

namespace bmock {
	struct st_recording : public test_mode_control::state_test {
		void set_result(test_mode_control *) const;
		void set_stub_recording_state(test_mode_control *) const;		
		void set_non_strict_recording_state(test_mode_control *) const;
	};
	struct st_rec_empty : public st_recording {
		void replay(test_mode_control *) const;
		void terminate(test_mode_control *) const;
		bool check_terminate(test_mode_control *) const;
		void change_state(test_mode_control *) const;
		static const state_test * const instance() { return &instance_; }
	private:
		static st_rec_empty instance_;
	};

	struct st_rec_non_empty : public st_recording {
		void replay(test_mode_control *) const;
		void validate_expect(test_mode_control *) const;
		void connect(test_mode_control *,const expectation::slot_type &) const;
		static const state_test * const instance() { return &instance_; }
	private:
		static st_rec_non_empty instance_;
	};
	struct st_recording_stub:public test_mode_control::state_test{
		void set_result(test_mode_control *) const;
		static const state_test * const instance() { return &instance_; }
	private:
		static st_recording_stub instance_;
	};

	struct st_recording_non_strict:public test_mode_control::state_test{
		void set_result(test_mode_control *) const;
		static const state_test * const instance() { return &instance_; }
	private:
		static st_recording_non_strict instance_;
	};

	struct st_rec_waiting_check : public test_mode_control::state_test {
		void check(test_mode_control *,const func_spec &,const arg_list &) const;
		static const state_test * const instance() { return &instance_; }
	private:
		static st_rec_waiting_check instance_;
	};

	struct st_rec_waiting_get_return : public test_mode_control::state_test {
		bmock_any get_result(test_mode_control *) const;
		void       return_void(test_mode_control *,const location &) const;

		static const state_test * const instance() { return &instance_; }
	private:
		static st_rec_waiting_get_return instance_;
	};

	struct st_rec_stub_waiting_check : public test_mode_control::state_test {
		void check(test_mode_control *,const func_spec &,const arg_list &) const;
		static const state_test * const instance() { return &instance_; }
	private:
		static st_rec_stub_waiting_check instance_;
	};

	struct st_rec_non_strict_waiting_check : public test_mode_control::state_test {
		void check(test_mode_control *,const func_spec &,const arg_list &) const;
		static const state_test * const instance() { return &instance_; }
	private:
		static st_rec_non_strict_waiting_check instance_;
	};	

	struct st_replaying : public test_mode_control::state_test {
		void verify(test_mode_control *) const;
	};

	struct st_rep_non_empty : public st_replaying {
		void check(test_mode_control *,const func_spec &,const arg_list & ) const;
		static const state_test * const instance() { return &instance_; }
	private:
		static st_rep_non_empty instance_;
	};

	struct st_rep_waiting_get_return : public st_replaying {
		bmock_any get_result(test_mode_control *) const;
		void       return_void(test_mode_control *,const location &) const;

		static const state_test * const instance() { return &instance_; }
	private:
		static st_rep_waiting_get_return instance_;
	};

	struct st_rep_empty : public test_mode_control::state_test {
		void check(test_mode_control *,const func_spec &,const arg_list &) const;
		void verify(test_mode_control *) const;

		static const state_test * const instance() { return &instance_; }
	private:
		static st_rep_empty instance_;
	};

	struct st_error : public test_mode_control::state_test {
		void check(test_mode_control *,const func_spec &,const arg_list &) const;
		void replay(test_mode_control *) const;
		void verify(test_mode_control *) const;
		void terminate(test_mode_control *) const;
		bool check_terminate(test_mode_control *) const;
		bmock_any get_result(test_mode_control *) const;
		void set_result(test_mode_control *) const;
		static const state_test * const instance() { return &instance_; }
	private:
		static st_error instance_;
	};

};

namespace bmock {
	void test_mode_control::state_test::init(test_mode_control *const ctl) {
		ctl->next_line();
		ctl->set_state(st_rec_empty::instance());
	}

	void test_mode_control::state_test::error(test_mode_control *const ctl,error_code e) const {
		ctl->set_state(st_error::instance());
		ctl->state_error(e);
	}
	void test_mode_control::state_test::check(test_mode_control *const ctl,const func_spec &f,const arg_list & args) const  { 
		ctl->set_state(st_error::instance());
		ctl->unexpected_call_before_replay(f,args);	
	}

	void test_mode_control::state_test::set_stub_recording_state(test_mode_control * const ctl)const
	{
		error(ctl,INVALID_STUB);
	}

	void test_mode_control::state_test::set_non_strict_recording_state(test_mode_control * const ctl)const
	{
		error(ctl,INVALID_NON_STRICT);
	}

	void test_mode_control::state_test::replay(test_mode_control *const ctl) const  { 
		error(ctl,INVALID_REPLAY);
	}
	void test_mode_control::state_test::verify(test_mode_control *const ctl) const  { 
		error(ctl,INVALID_VERIFY);
	}
	void test_mode_control::state_test::terminate(test_mode_control *const ctl) const  {
		ctl->next_line();
		ctl->state_error(INVALID_TERMINATE);
	}
	bool test_mode_control::state_test::check_terminate(test_mode_control *const ctl) const {
		ctl->set_state(st_error::instance());
		return false;
	}
	void test_mode_control::state_test::set_result(test_mode_control *const ctl) const {
		error(ctl,INVALID_EXPECT);
	}
	bmock_any test_mode_control::state_test::get_result(test_mode_control *const ctl) const {
		error(ctl,INVALID_GET_RESULT);
		return bmock_any(); 
	}
	void test_mode_control::state_test::return_void(test_mode_control *const,const location &) const {
	}
	void test_mode_control::state_test::validate_expect(test_mode_control *const ctl) const {
		error(ctl,EXPECT_FOR_NON_MOCK);
	}
	void test_mode_control::state_test::connect(test_mode_control *ctl,const expectation::slot_type &) const {
		error(ctl,INVALID_EXPECT_CALLBACK);
	}

	void st_recording::set_result(test_mode_control *const ctl) const {
		ctl->add_expectation();
		ctl->save_result();
		ctl->set_state(st_rec_waiting_check::instance());
	}
	void st_recording::set_stub_recording_state(test_mode_control *ctl) const
	{
		ctl->set_state(st_recording_stub::instance()); 
	}

	void st_recording::set_non_strict_recording_state(test_mode_control *ctl) const
	{
		ctl->set_state(st_recording_non_strict::instance()); 
	}

	void st_rec_empty::replay(test_mode_control *const ctl) const  {
		ctl->next_line();
		ctl->set_state(st_rep_empty::instance()); 
	}

	void st_rec_empty::terminate(test_mode_control *const) const  { 
	}
	bool st_rec_empty::check_terminate(test_mode_control *const) const {
		return true;
	}
	st_rec_empty st_rec_empty::instance_;

	void st_rec_non_empty::replay(test_mode_control *const ctl) const  {
		ctl->next_line();
		ctl->set_state(st_rep_non_empty::instance()); 
	}
	void st_rec_non_empty::validate_expect(test_mode_control *const) const  {
	}
	void st_rec_non_empty::connect(test_mode_control *ctl,const expectation::slot_type &slot) const {
		ctl->connect_callback(slot);
	}
	st_rec_non_empty st_rec_non_empty::instance_;

	void st_rec_waiting_check::check(test_mode_control *const ctl,const func_spec &f,const arg_list &args) const  { 
		try {
			ctl->expect_call(f,args);
		} catch (const location &l) {
			ctl->set_state(st_error::instance());
			ctl->abort(l);
		} catch (...) {
			ctl->set_state(st_error::instance());
			throw;
		}
		ctl->set_state(st_rec_waiting_get_return::instance());
	}
	st_rec_waiting_check st_rec_waiting_check::instance_;

	bmock_any st_rec_waiting_get_return::get_result(test_mode_control *const ctl) const {
		ctl->set_state(st_rec_non_empty::instance());
		try {
			return ctl->get_result_value();
		} catch (const void_result *) {
			error(ctl,CHECK_RETURN_REQUIRED);
		} catch (const exception_result *) {
			bmock_mutex::instance()->unlock(); 
			throw;
		}
		return bmock_any();
	}

	void st_rec_waiting_get_return::return_void(test_mode_control *const ctl,const location &loc) const {
		try {
			ctl->get_result_value();
		} catch (const result *) {
			ctl->set_state(st_rec_non_empty::instance());
			return;
		}
		ctl->set_state(st_error::instance());
		ctl->state_error(CHECK_RETURN_REQUIRED,loc);
	}
	st_rec_waiting_get_return st_rec_waiting_get_return::instance_;

	void st_replaying::verify(test_mode_control *const ctl) const  { 
		if (ctl->is_done()) {
			ctl->next_line();
			ctl->set_state(st_rec_empty::instance());
		}
		else {
			ctl->set_state(st_error::instance());
			ctl->not_received();
		}
	}

	void st_rep_non_empty::check(test_mode_control *const ctl,const func_spec &f,const arg_list &args) const  { 
		try {
			ctl->check_call(f,args);
			ctl->set_state(st_rep_waiting_get_return::instance());
		} catch (const location &loc) {
			ctl->set_state(st_error::instance());
			ctl->abort(loc);
		} catch (...) {
			ctl->set_state(st_error::instance());
			throw;
		}
	}
	st_rep_non_empty st_rep_non_empty::instance_;

	bmock_any st_rep_waiting_get_return::get_result(test_mode_control *const ctl) const {
		if(ctl->is_empty() && ctl->non_strict_is_empty() && ctl->stub_is_empty()) {
			ctl->prev_line();
			ctl->set_state(st_rep_empty::instance());
		} else
			ctl->set_state(st_rep_non_empty::instance());
		try {
			return ctl->get_result_value();
		} catch (const exception_result *e) {
			bmock_mutex::instance()->unlock(); 
			e->throw_exception();
		}
		return bmock_any();
	}

	void st_rep_waiting_get_return::return_void(test_mode_control *const ctl,const location &) const {
		if(ctl->is_empty() && ctl->non_strict_is_empty() && ctl->stub_is_empty() ) {
			ctl->prev_line();
			ctl->set_state(st_rep_empty::instance());
		} else
			ctl->set_state(st_rep_non_empty::instance());
		try {
			ctl->get_result_value();
		} catch (const void_result *) {
		} catch (const exception_result *e) {
			bmock_mutex::instance()->unlock(); 
			e->throw_exception();
		}
	}
	st_rep_waiting_get_return st_rep_waiting_get_return::instance_;

	void st_rep_empty::check(test_mode_control *const ctl,const func_spec &f,const arg_list &args) const  { 
		ctl->set_state(st_error::instance());
		ctl->unexpected_call(f,args);
	}
	void st_rep_empty::verify(test_mode_control *const ctl) const  {
		ctl->next_line();
		ctl->set_state(st_rec_empty::instance());
	}
	st_rep_empty st_rep_empty::instance_;

	void st_error::check(test_mode_control *const,const func_spec &,const arg_list &) const  { 
	}
	void st_error::replay(test_mode_control *const) const  { 
	}
	void st_error::verify(test_mode_control *const) const  { 
	}
	void st_error::terminate(test_mode_control *const) const  { 
	}
	bool st_error::check_terminate(test_mode_control *const) const  { 
		return true;
	}

	bmock_any st_error::get_result(test_mode_control *) const {
		throw boost::execution_aborted();
		return bmock_any(); 
	}

	void st_error::set_result(test_mode_control *) const {
	}

	st_error st_error::instance_;

	void st_recording_stub::set_result(test_mode_control *const ctl) const {
		ctl->add_stub();
		ctl->save_stub_result();
		ctl->set_state(st_rec_stub_waiting_check::instance());
	}

	st_recording_stub st_recording_stub::instance_;

	void st_rec_stub_waiting_check::check(test_mode_control *const ctl,const func_spec &f,const arg_list &args) const  { 
		try {
			ctl->expect_stub_call(f,args);
		} catch (const location &l) {
			ctl->set_state(st_error::instance());
			ctl->abort(l);
		} catch (...) {
			ctl->set_state(st_error::instance());
			throw;
		}
		ctl->set_state(st_rec_waiting_get_return::instance());
	}

	st_rec_stub_waiting_check st_rec_stub_waiting_check::instance_;

	void st_recording_non_strict::set_result(test_mode_control *const ctl) const {
		ctl->add_non_strict();
		ctl->save_non_strict_result();
		ctl->set_state(st_rec_non_strict_waiting_check::instance());
	}

	st_recording_non_strict st_recording_non_strict::instance_;

	void st_rec_non_strict_waiting_check::check(test_mode_control *const ctl,const func_spec &f,const arg_list &args) const  { 
		try {
			ctl->expect_non_strict_call(f,args);
		} 
		catch (const location &l) {
			ctl->set_state(st_error::instance());
			ctl->abort(l);
		} 
		catch (...) {
			ctl->set_state(st_error::instance());
			throw;
		}
		ctl->set_state(st_rec_waiting_get_return::instance());

	}

	st_rec_non_strict_waiting_check st_rec_non_strict_waiting_check::instance_;

}